﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Data.Common;
using PetaPoco.Internal;
using PetaPoco;

namespace Demo.Data
{
    public class MatrixData : PetaPoco.Database
    {
        #region 构造函数

        public MatrixData(IDbConnection connection)
            : base(connection)
        {
        }

        public MatrixData(string connectionString, string providerName)
            : base(connectionString, providerName)
        {
        }

        public MatrixData(string connectionString, DbProviderFactory provider)
            : base(connectionString, provider)
        {
        }

        public MatrixData(string connectionStringName)
            : base(connectionStringName)
        {
        }

        #endregion

        #region 构建新实例

        public static MatrixData Create(IDbConnection connection)
        {
            return new MatrixData(connection);
        }

        public static MatrixData Create(string connectionString, string providerName)
        {
            return new MatrixData(connectionString, providerName);
        }

        public static MatrixData Create(string connectionString, DbProviderFactory provider)
        {
            return new MatrixData(connectionString, provider);
        }

        public static MatrixData Create(string connectionStringName)
        {
            return new MatrixData(connectionStringName);
        }

        #endregion

        #region 获取DataTable的方法

        /// <summary>
        /// 得到一个DataTable
        /// </summary>
        /// <typeparam name="T">根据类型T自动生成select语句</typeparam>
        /// <param name="sql">Sql语句</param>
        /// <param name="args">参数</param>
        public DataTable FetchDataTable<T>(string sql, params object[] args)
        {
            if (EnableAutoSelect)
                sql = AutoSelectHelper.AddSelectClause<T>(_dbType, sql);

            return FetchDataTable(sql, args);
        }


        public DataTable FetchDataTable<T>(Sql sql)
        {
            return FetchDataTable<T>(sql.SQL, sql.Arguments);
        }


        public DataTable FetchDataTable(Sql sql)
        {
            return FetchDataTable(sql.SQL, sql.Arguments);
        }

        /// <summary>
        /// 得到一个DataTable
        /// </summary>
        /// <param name="sql">Sql语句</param>
        /// <param name="args">参数</param>
        public DataTable FetchDataTable(string sql, params object[] args)
        {
            DataTable dt = new DataTable();

            OpenSharedConnection();

            try
            {
                using (var cmd = CreateCommand(_sharedConnection, sql, args))
                {
                    using (DbDataAdapter dbDataAdapter = _factory.CreateDataAdapter())
                    {
                        dbDataAdapter.SelectCommand = (DbCommand)cmd;
                        dbDataAdapter.Fill(dt);
                    }
                }
            }
            finally
            {
                CloseSharedConnection();
            }

            return dt;
        }

        #endregion

        #region 获取分页数据

        public MatrixDataPage FetchPageDataTable(long page, long itemsPerPage, string sqlCount, object[] countArgs, string sqlPage, object[] pageArgs)
        {
            // Save the one-time command time out and use it for both queries
            var saveTimeout = OneTimeCommandTimeout;

            // Setup the paged result
            var result = new MatrixDataPage
            {
                CurrentPage = page,
                ItemsPerPage = itemsPerPage,
                TotalItems = ExecuteScalar<long>(sqlCount, countArgs)
            };
            result.TotalPages = result.TotalItems / itemsPerPage;

            if ((result.TotalItems % itemsPerPage) != 0)
                result.TotalPages++;

            OneTimeCommandTimeout = saveTimeout;

            // Get the records
            result.Data = FetchDataTable(sqlPage, pageArgs);

            // Done
            return result;
        }

        public MatrixDataPage FetchPageDataTable<T>(long page, long itemsPerPage, string sql, params object[] args)
        {
            string sqlCount, sqlPage;
            BuildPageQueries<T>((page -1) * itemsPerPage, itemsPerPage, sql, ref args, out sqlCount, out sqlPage);
            return FetchPageDataTable(page, itemsPerPage, sqlCount, args, sqlPage, args);
        }

        public MatrixDataPage FetchPageDataTable(long page, long itemsPerPage, string sql, params object[] args)
        {
            string sqlCount, sqlPage;
            BuildPageQueries((page -1) * itemsPerPage, itemsPerPage, sql, ref args, out sqlCount, out sqlPage);
            return FetchPageDataTable(page, itemsPerPage, sqlCount, args, sqlPage, args);
        }

        public MatrixDataPage FetchPageDataTable<T>(long page, long itemsPerPage, Sql sql)
        {
            return FetchPageDataTable<T>(page, itemsPerPage, sql.SQL, sql.Arguments);
        }

        public MatrixDataPage FetchPageDataTable(long page, long itemsPerPage, Sql sql)
        {
            return FetchPageDataTable(page, itemsPerPage, sql.SQL, sql.Arguments);
        }

        public MatrixDataPage FetchPageDataTable<T>(long page, long itemsPerPage, Sql sqlCount, Sql sqlPage)
        {
            return FetchPageDataTable<T>(page, itemsPerPage, sqlCount.SQL, sqlCount.Arguments, sqlPage.SQL, sqlPage.Arguments);
        }

        public MatrixDataPage FetchPageDataTable(long page, long itemsPerPage, Sql sqlCount, Sql sqlPage)
        {
            return FetchPageDataTable(page, itemsPerPage, sqlCount.SQL, sqlCount.Arguments, sqlPage.SQL, sqlPage.Arguments);
        }

        /// <summary>
        /// Starting with a regular SELECT statement, derives the SQL statements required to query a 
        /// DB for a page of records and the total number of records
        /// </summary>
        /// <typeparam name="T">The Type representing a row in the result set</typeparam>
        /// <param name="skip">The number of rows to skip before the start of the page</param>
        /// <param name="take">The number of rows in the page</param>
        /// <param name="sql">The original SQL select statement</param>
        /// <param name="args">Arguments to any embedded parameters in the SQL</param>
        /// <param name="sqlCount">Outputs the SQL statement to query for the total number of matching rows</param>
        /// <param name="sqlPage">Outputs the SQL statement to retrieve a single page of matching rows</param>
        protected void BuildPageQueries(long skip, long take, string sql, ref object[] args, out string sqlCount, out string sqlPage)
        {
            // Split the SQL
            PagingHelper.SQLParts parts;
            if (!PagingHelper.SplitSQL(sql, out parts))
                throw new Exception("Unable to parse SQL statement for paged query");

            sqlPage = _dbType.BuildPageQuery(skip, take, parts, ref args);
            sqlCount = parts.sqlCount;
        }

        #endregion

        #region 重写获取错误的方法

        public override bool OnException(Exception x)
        {
            return base.OnException(x);
        }

        public override IDbConnection OnConnectionOpened(IDbConnection conn)
        {
            return base.OnConnectionOpened(conn);
        }

        public override void OnConnectionClosing(IDbConnection conn)
        {
            base.OnConnectionClosing(conn);
        }

        public override void OnExecutingCommand(IDbCommand cmd)
        {
            base.OnExecutingCommand(cmd);
        }

        public override void OnExecutedCommand(IDbCommand cmd)
        {
            base.OnExecutedCommand(cmd);
        }

        public override void OnBeginTransaction()
        {
            base.OnBeginTransaction();
        }

        public override void OnEndTransaction()
        {
            base.OnEndTransaction();
        }

        #endregion
    }
}
